package substr

// Append a new substring interval. The interval must be after the last interval
//Fails if the range is invalid for any reason. See invariants
//on the S type. Returns true iff the range is added.
//If end == -1 then end = len(s.Super)
func (s *S) Push(start, end int) bool {
	if end == -1 {
		end = len(s.Super)
	}
	if start < 0 || end < start || len(s.Super) < end {
		return false
	}
	ln := len(s.Subs)
	if ln > 0 && start < s.Subs[ln-1][1] {
		//overlaps previous match
		return false
	}
	if c := cap(s.Subs); ln == c {
		new := make([][]int, ln, c + c/2)
		copy(new, s.Subs)
		s.Subs = new
	}
	s.Subs = s.Subs[:ln+1]
	s.Subs[ln] = []int{start, end}
	return true
}

// A helper for Finders. Push from the end of the last index to the end of the
//string, if given true. Why it takes an argument at all may seem
//counterintutive, so here is the rationale:
//	* PushRest is only helpful in finders
//	* It depends on the number of matches returned parameter, n, in almost all cases.
//	* A finder usually has to calculate a reasonable value for n when n < 0 to simplify loop conditions.
//	* If n >= 0 often you do not want to call PushRest.
// So, the options, given the above, are to store a copy of n and test the copy
//being less than 0 and apply PushRest if true or store the result of the
//original comparison. It would in most cases being an error to call PushRest
//when n >= 0, and no great imposition to call it with true if it is desired
//that it always be invoked, so given the above the following idiom is baked in:
//	all := n < 0
//	if all { n = f() }
//	//collect substrings into out
//	return out.Trunc(n).PushRest(!all) //if Trunc need be called
//
//Handles all annoying special cases.
func (s *S) PushRest(fire bool) *S {
	if fire {
		ln, last := len(s.Super), 0
		if iln := len(s.Subs); iln != 0 {
			last = s.Subs[iln-1][1]
		}
		if last != ln {
			s.Push(last, -1)
		}
	}
	return s
}

// Remove and return last substring interval. Must be at least one substring.
func (s *S) Pop() (i, j int) {
	end := len(s.Subs) - 1
	i, j = s.Subs[end][0], s.Subs[end][1]
	s.Subs[end] = nil
	s.Subs = s.Subs[:end]
	return
}

// Replace substring i with (a, b), if valid. There must be an ith substring.
// Returns false if nothing was set or true if i is now (a, b).
func (s *S) Set(i, a, b int) bool {
	ln := len(s.Subs)
	if a < 0 || b < a || len(s.Super)-1 < b {
		return false
	}
	if i == 0 {
		if ln != 1 && s.Subs[1][0] < b {
			return false
		}
	} else if i == ln-1 {
		if a <= s.Subs[ln-2][1] {
			return false
		}
	} else if a < s.Subs[i-1][1] || s.Subs[i+1][0] < b {
		return false
	}
	s.Subs[i] = []int{a, b}
	return true
}

// Insert (a,b) at postion i, iff it maintains invariants.
// The ith substring before insertion is the (i+1)th substring after.
// Can only insert at a valid index or one after a valid index.
func (s *S) Insert(i, a, b int) bool {
	ln := len(s.Subs)
	if a < 0 || b < a || len(s.Super) < b {
		return false
	}
	left := i == 0 || s.Subs[i-1][1] <= a
	right := i == ln-1 || i == ln || b <= s.Subs[i+1][0]
	if !(left && right) {
		return false
	}
	if c := cap(s.Subs); ln == c {
		new := make([][]int, ln+1, c + c/2)
		copy(new, s.Subs[0:i])
		if i < ln {
			copy(new[i+1:], s.Subs[i:])
		}
		s.Subs = new
	} else {
		s.Subs = s.Subs[0 : ln+1]
		if i < ln {
			copy(s.Subs[i+1:], s.Subs[i:])
		}
	}
	s.Subs[i] = []int{a, b}
	return true
}

// Remove the ith substring. The gap is closed so the old element at index i+1
//has index i afterwards. i must be a valid index
// The operation is inplace and returns itself for chaining.
func (s *S) Delete(i int) *S {
	ln := len(s.Subs)
	copy(s.Subs[i:ln], s.Subs[i+1:ln])
	s.Subs[ln-1] = nil
	s.Subs = s.Subs[:ln-1]
	return s
}

// Remove the substrings in the range [i, j). If j == -1 then j = len
// The operation is inplace and returns itself for chaining.
func (s *S) Cut(i, j int) *S {
	ln := len(s.Subs)
	if j == -1 || j == ln {
		//removing end, just reslice
		for n := i; n < ln; n++ {
			s.Subs[n] = nil
		}
		s.Subs = s.Subs[:i]
	} else if i == 0 {
		copy(s.Subs, s.Subs[j:])
		for n := j + 1; n < ln; n++ {
			s.Subs[n] = nil
		}
		s.Subs = s.Subs[:j+1]
	} else {
		//in the middle, have to be a little fancier:
		//given [===A===]--B--[===C===] copy C over B
		//then slice the left overs off
		end := ln - copy(s.Subs[i:], s.Subs[j:])
		for n := end + 1; n < ln; n++ {
			s.Subs[n] = nil
		}
		s.Subs = s.Subs[:end]
	}
	return s
}

//Truncate the indicies to no more than n. If n < 0 leave them as they be.
//Helper to implement Finders that cannot know how many substrings they
//create until after they have been created. Returns self for chaining.
func (s *S) Trunc(n int) *S {
	if n >= 0 && n < len(s.Subs) {
		s.Cut(n, -1)
	}
	return s
}
